﻿/************************************************
 * CODE MODIFIED FROM: http://madskristensen.net/post/A-realtime-currency-exchange-class-in-C.aspx
 * Originally created by: Mads Kristensen, 2006
 * Modified By: Milo Eaton, 2009
 * **********************************************/
using System;
using System.IO;
using System.Configuration;
using System.Xml;
using System.Collections.Generic;
using System.Timers;
using System.Text;
using System.Net;

/// <summary>
/// Maintains a list of currencies and exchange rates
/// and keeps it up to date. 
/// </summary>
public class CurrencyExchange {

	public event EventHandler UpdateComplete;

	private SortedDictionary<string, double> m_Currencies = new SortedDictionary<string, double>();
	private DateTime m_LastUpdated;
	private DateTime m_LastModified;
	private string m_XML = null;
	private string m_Url = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml";

	#region " Properties "

	public SortedDictionary<string, double> Currencies {
		get { return m_Currencies; }
	}

	/// <summary>
	/// Gets the date of the current exchange rates.
	/// </summary>
	public DateTime LastUpdated {
		get { return m_LastUpdated; }
	}

	public string XML {
		get { return m_XML; }
		set { m_XML = value; }
	}

	public string Url {
		get { return m_Url; }
		set { m_Url = value; }
	}

	#endregion

	#region " Constructors "

	public CurrencyExchange() {
		Download();
		ProcessXml();
	}

	public CurrencyExchange( string xml ) {
		m_XML = xml;
		ProcessXml();
	}

	#endregion

	#region " Events "

	/// <summary>
	/// Occurs when the currencies has been updated
	/// </summary>
	private void OnUpdateComplete() {
		if ( UpdateComplete != null ) {
			UpdateComplete( null, new EventArgs() );
		}
	}

	#endregion

	#region " Methods "

	/// <summary>
	/// Converts an amount of one currency to another.
	/// </summary>
	public double Exchange( string fromCurrency, string toCurrency, double value ) {
		double from = m_Currencies[fromCurrency.ToUpperInvariant()];
		double to = m_Currencies[toCurrency.ToUpperInvariant()];
		return value * ( to / from );
	}

	/// <summary>
	/// Get the exchange rate for one single currency.
	/// The rate is relative to the EURO.
	/// </summary>
	/// <param name="currency">The three letter ISO code.</param>
	/// <returns>The rate of the currency relative to the EURO.</returns>
	public double GetRate( string currency ) {
		return m_Currencies[currency.ToUpperInvariant()];
	}

	/// <summary>
	/// Get the exchange rate for one single currency, based
	/// on another currency.
	/// </summary>
	/// <param name="currency">The currency rate you want.</param>
	/// <param name="baseCurrency">The currency to base it on.</param>
	/// <returns>The rate of the currency relative to the baseCurrency.</returns>
	public double GetRate( string currency, string baseCurrency ) {
		double from = m_Currencies[currency.ToUpperInvariant()];
		double to = m_Currencies[baseCurrency.ToUpperInvariant()];
		return to / from;
	}

	/// <summary>
	/// Download the lates exchange rates and
	/// parse the downloaded XML file.
	/// </summary>
	public void Update() {
		Download();
	}

	/// <summary>
	/// Parse the downloaded XML file.
	/// </summary>
	private void ProcessXml() {
		if ( m_XML == null ) {
			throw new Exception( "The currencies haven't been downloaded." );
		}
		SortedDictionary<string, double> currencies = new SortedDictionary<string, double>();
		currencies.Add( "EUR", 1.0 );
		using ( StringReader sr = new StringReader( m_XML ) ) {
			using ( XmlTextReader xmlReader = new XmlTextReader( sr ) ) {
				while ( xmlReader.Read() ) {
					for ( int i = 0; i < xmlReader.AttributeCount; i++ ) {
						AddCurrency( currencies, xmlReader );
						xmlReader.MoveToNextAttribute();
					}
				}
			}
		}
		m_Currencies = currencies;
		OnUpdateComplete();
	}

	/// <summary>
	/// Reads through the XML file and adds the new
	/// exchange rates to the list.
	/// </summary>
	private void AddCurrency( SortedDictionary<string, double> currencies, XmlTextReader xmlReader ) {
		if ( xmlReader.Name == "Cube" ) {
			if ( xmlReader.AttributeCount == 1 ) {
				xmlReader.MoveToAttribute( "time" );
				m_LastUpdated = DateTime.Parse( xmlReader.Value );
			}
			if ( xmlReader.AttributeCount == 2 ) {
				xmlReader.MoveToAttribute( "currency" );
				string name = xmlReader.Value;
				xmlReader.MoveToAttribute( "rate" );
				double rate = double.Parse( xmlReader.Value.Replace( ".", "," ) );
				currencies.Add( name, rate );
			}
		}
	}

	/// <summary>
	/// Downloads the latest exchange rates from ECB.
	/// </summary>
	private void Download() {
		HttpWebRequest request = WebRequest.Create( m_Url ) as HttpWebRequest;
		using ( HttpWebResponse response = request.GetResponse() as HttpWebResponse ) {
			if ( response.StatusCode == HttpStatusCode.OK && response.LastModified != m_LastModified ) {
				StringBuilder sb = new StringBuilder();
				using ( StringWriter writer = new StringWriter( sb ) ) {
					StreamReader reader = new StreamReader( response.GetResponseStream() );
					writer.Write( reader.ReadToEnd() );
					reader.Close();
				}
				m_XML = sb.ToString();
				m_LastModified = response.LastModified;
			}
		}
	}

	#endregion

}